﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Damany.Cameras;
using LprWrapperSharp;

namespace CheckingLine
{
    public class LprCapturedEventArgs : EventArgs
    {
        public Image Image { get; private set; }
        public RecognizeResult Result { get; private set; }

        public LprCapturedEventArgs(Image image, LprWrapperSharp.RecognizeResult result)
        {
            Image = image;
            Result = result;
        }
    }

    class Worker
    {
        private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
        private readonly string _requestFileFullPath;
        private readonly string _resultFileFullPath;
        private readonly string _cameraIp;
        private readonly int _cameraPort;
        private readonly int _cameraStreamId;
        private readonly string _userName;
        private readonly string _password;
        private readonly Rectangle _roi;
        private readonly string _provience;
        private CancellationTokenSource _cts;
        private FoKoShCamera _camera;
        private static LprRecognizer _lprRecognizer;
        private BlockingCollection<Image> _waitingImageCollection;
        private string _lastImageFullPath;

        public event EventHandler<LprCapturedEventArgs> LprCapturred;

        protected virtual void OnLprCapturred(Image img, RecognizeResult lpr)
        {
            EventHandler<LprCapturedEventArgs> handler = LprCapturred;
            if (handler != null) handler(this, new LprCapturedEventArgs(img, lpr));
        }

        public event EventHandler<EventArgs> RequestFound;

        protected virtual void OnRequestFound()
        {
            EventHandler<EventArgs> handler = RequestFound;
            if (handler != null) handler(this, EventArgs.Empty);
        }


        public Action<string> Logger { get; set; }




        public Worker(
            string requestFileFullPath, string resultFileFullPath,
            string cameraIp, int cameraPort, int cameraStreamId, string userName, string password,
            Rectangle roi, string provience)
        {
            _requestFileFullPath = requestFileFullPath;
            _resultFileFullPath = resultFileFullPath;
            _cameraIp = cameraIp;
            _cameraPort = cameraPort;
            _cameraStreamId = cameraStreamId;
            _userName = userName;
            _password = password;
            _roi = roi;
            _provience = provience;

            Logger = s => { };

        }

        public void Start()
        {
            _waitingImageCollection = new BlockingCollection<Image>();
            _cts = new CancellationTokenSource();

            Task.Factory.StartNew(ImageCapturerWorkerThread, _cts, TaskCreationOptions.LongRunning);
            //RecognizerWorkerThread(null);
            Task.Factory.StartNew(RecognizerWorkerThread, _cts, TaskCreationOptions.LongRunning);
        }

        public void Stop()
        {
            _cts.Cancel();
            _waitingImageCollection.CompleteAdding();
        }


        public void TriggerOnce()
        {
            if (File.Exists(_resultFileFullPath))
            {
                File.Delete(_resultFileFullPath);
            }

            if (!File.Exists(_requestFileFullPath))
            {
                File.WriteAllBytes(_requestFileFullPath, new byte[0]);
            }
        }

        public void AddImage(Image img)
        {
            var clone = (Image)img.Clone();
            _waitingImageCollection.Add(clone);
        }

        private void RecognizerWorkerThread(object state)
        {
            try
            {

                foreach (var image in _waitingImageCollection.GetConsumingEnumerable(_cts.Token))
                {
                    var imgBound = new Rectangle(0, 0, image.Width, image.Height);
                    imgBound.Intersect(_roi);
                    if (imgBound.Width < 90)
                    {
                        Logger("警告 - 识别区域宽度过小 " + imgBound.Width + "像素");
                    }
                    var res = _lprRecognizer.RecogImage((Bitmap)image.Clone(), imgBound);
                    OnLprCapturred((Image)image.Clone(), res);
                    SaveResult(res, image);
                    image.Dispose();
                }
            }
            catch (Exception ex)
            {
                var msg = "识别线程发生异常，退出工作。";
                Logger(msg);
                _logger.ErrorException(msg, ex);
            }

        }

        private void ImageCapturerWorkerThread(object state)
        {
            try
            {
                Logger("开始初始化相机");
                InitCamera();
                Logger("初始化相机成功，开始等待开始信号");
                while (true)
                {
                    _cts.Token.ThrowIfCancellationRequested();
                    WaitForGoSignal();
                    Logger("获得开始信号，开始抓拍图片");
                    CaptureCameraImage();
                }
            }
            catch (Exception ex)
            {
                Logger("相机图片获取线程发生异常，结束运行：" + ex.Message);
            }

        }

        private void InitCamera()
        {
            _camera = new FoKoShCamera(IntPtr.Zero);
            _camera.Ip = _cameraIp;
            _camera.Port = _cameraPort;
            _camera.StreamId = _cameraStreamId;
            _camera.UserName = _userName;
            _camera.Password = _password;
            _camera.Start();

        }

        public static void InitRecognizer()
        {
            _logger.Trace("开始初始化车牌识别模块");
            _lprRecognizer = new LprRecognizer();
            _lprRecognizer.SetImageFormat(ImageFormat.BGR, false, true);
            _lprRecognizer.SetCharacterPriority(Properties.Settings.Default.Provience);
            _logger.Trace("初始化车牌识别模块：成功");
        }

        private void SaveResult(RecognizeResult res, Image image)
        {
            const string sectionName = "RecognizeResult";
            var imagePath = string.Empty;
            var lprNumber = string.Empty;

            lprNumber = res.Succeed ? res.PlateNumber : string.Empty;

            var imgDir = Path.GetDirectoryName(_resultFileFullPath);
            var imgPath = Path.Combine(imgDir, Guid.NewGuid() + ".jpg");
            image.Save(imgPath, System.Drawing.Imaging.ImageFormat.Jpeg);
            imagePath = imgPath;
            _lastImageFullPath = imagePath;

            var iniData = new IniParser.IniData();
            iniData.Sections.AddSection(sectionName);
            iniData[sectionName].AddKey("success", res.Succeed.ToString());
            iniData[sectionName].AddKey("imagePath", imagePath);
            iniData[sectionName].AddKey("lprNumber", lprNumber);

            var writer = new IniParser.FileIniDataParser();
            var tmpFile = Path.GetTempFileName();

            try
            {
                using (var fs = File.OpenWrite(tmpFile))
                using (var sw = new StreamWriter(fs, Encoding.GetEncoding("GB2312")))
                {
                    writer.WriteData(sw, iniData);
                }

                var tempTargetFile = _resultFileFullPath + ".tmp";
                File.Move(tmpFile, tempTargetFile);
                File.Move(tempTargetFile, _resultFileFullPath);
            }
            catch (Exception ex)
            {
                Logger("保存结果文件发生异常：" + ex.Message);
            }

        }

        private void CaptureCameraImage()
        {
            Logger("开始从相机获取图片");
            var img = _camera.CaptureImage();
            if (!_waitingImageCollection.IsAddingCompleted)
            {
                _waitingImageCollection.Add(img);
            }
            Logger("相机图片获取成功");
        }

        private void WaitForGoSignal()
        {
            bool stateChanged = true;
            while (true)
            {
                _cts.Token.ThrowIfCancellationRequested();
                if (stateChanged)
                {
                    Logger("开始查询请求文件和结果文件状态");
                    stateChanged = false;
                }

                var requestFound = File.Exists(_requestFileFullPath); //约定为请求文件出现，结果文件被删除
                var resultNotFound = !File.Exists(_resultFileFullPath);
                if (requestFound && resultNotFound)
                {
                    Logger("请求文件(存在)和结果文件(不存在)状态满足要求");
                    DeleteRequest();
                    return;
                }
                Thread.Sleep(200);
            }
        }

        private void DeleteRequest()
        {
            while (true)
            {
                _cts.Token.ThrowIfCancellationRequested();
                try
                {
                    if (_lastImageFullPath != null && File.Exists(_lastImageFullPath))
                    {
                        File.Delete(_lastImageFullPath);
                    }
                    File.Delete(_requestFileFullPath);
                    Logger("请求文件删除成功");
                    return;
                }
                catch (Exception ex)
                {
                    Logger("删除请求文件出错，稍后重试: " + ex.Message);
                    Thread.Sleep(1000);
                }
            }
        }
    }
}
